1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.codehaus.groovy.transform;
20
21 import groovy.transform.AnnotationCollector;
22
23 import java.lang.reflect.Method;
24 import java.util.*;
25
26 import org.codehaus.groovy.GroovyBugError;
27 import org.codehaus.groovy.ast.*;
28 import org.codehaus.groovy.ast.expr.*;
29 import org.codehaus.groovy.ast.stmt.ReturnStatement;
30 import org.codehaus.groovy.ast.stmt.Statement;
31 import org.codehaus.groovy.control.SourceUnit;
32 import org.codehaus.groovy.control.messages.SyntaxErrorMessage;
33 import org.codehaus.groovy.syntax.SyntaxException;
34
35 import static org.objectweb.asm.Opcodes.*;
36
37
38
39
40
41
42
43 public class AnnotationCollectorTransform {
44
45 private static List<AnnotationNode> getMeta(ClassNode cn) {
46 List<AnnotationNode> meta = cn.getNodeMetaData(AnnotationCollector.class);
47 if (meta == null) {
48 if (cn.isPrimaryClassNode()) {
49 meta = getTargetListFromAnnotations(cn);
50 } else {
51 meta = getTargetListFromClass(cn);
52 }
53 cn.setNodeMetaData(AnnotationCollector.class, meta);
54 }
55 return meta;
56 }
57
58
59
60
61
62
63
64
65
66
67 public static class ClassChanger {
68
69
70
71
72
73
74 public void transformClass(ClassNode cn) {
75 AnnotationNode collector = null;
76 for (ListIterator<AnnotationNode> it = cn.getAnnotations().listIterator(); it.hasNext();) {
77 AnnotationNode an = it.next();
78 if (an.getClassNode().getName().equals(AnnotationCollector.class.getName())) {
79 collector = an;
80 break;
81 };
82 }
83 if (collector==null) return;
84
85
86 cn.setModifiers((ACC_FINAL+cn.getModifiers()) & ~(ACC_ENUM|ACC_INTERFACE|ACC_ANNOTATION|ACC_ABSTRACT));
87
88 cn.setSuperClass(ClassHelper.OBJECT_TYPE);
89
90 cn.setInterfaces(ClassNode.EMPTY_ARRAY);
91
92
93 List<AnnotationNode> meta = getMeta(cn);
94 List<Expression> outer = new ArrayList<Expression>(meta.size());
95 for (AnnotationNode an : meta) {
96 Expression serialized = serialize(an);
97 outer.add(serialized);
98 }
99
100 ArrayExpression ae = new ArrayExpression(ClassHelper.OBJECT_TYPE.makeArray(), outer);
101 Statement code = new ReturnStatement(ae);
102 cn.addMethod( "value", ACC_PUBLIC+ACC_STATIC,
103 ClassHelper.OBJECT_TYPE.makeArray().makeArray(),
104 Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY,
105 code);
106
107
108 for (ListIterator<AnnotationNode> it = cn.getAnnotations().listIterator(); it.hasNext();) {
109 AnnotationNode an = it.next();
110 if (an==collector) continue;
111 it.remove();
112 }
113 }
114
115 private Expression serialize(Expression e) {
116 if (e instanceof AnnotationConstantExpression) {
117 AnnotationConstantExpression ace = (AnnotationConstantExpression) e;
118 return serialize((AnnotationNode) ace.getValue());
119 } else if (e instanceof ListExpression) {
120 boolean annotationConstant = false;
121 ListExpression le = (ListExpression) e;
122 List<Expression> list = le.getExpressions();
123 List<Expression> newList = new ArrayList<Expression>(list.size());
124 for (Expression exp: list) {
125 annotationConstant = annotationConstant || exp instanceof AnnotationConstantExpression;
126 newList.add(serialize(exp));
127 }
128 ClassNode type = ClassHelper.OBJECT_TYPE;
129 if (annotationConstant) type = type.makeArray();
130 return new ArrayExpression(type, newList);
131 }
132 return e;
133 }
134
135 private Expression serialize(AnnotationNode an) {
136 MapExpression map = new MapExpression();
137 for (String key : an.getMembers().keySet()) {
138 map.addMapEntryExpression(new ConstantExpression(key), serialize(an.getMember(key)));
139 }
140 List<Expression> l = new ArrayList<Expression>(2);
141 l.add(new ClassExpression(an.getClassNode()));
142 l.add(map);
143 ArrayExpression ae = new ArrayExpression(ClassHelper.OBJECT_TYPE, l);
144 return ae;
145 }
146 }
147
148
149
150
151
152
153
154
155 protected void addError(String message, ASTNode node, SourceUnit source) {
156 source.getErrorCollector().addErrorAndContinue(new SyntaxErrorMessage(new SyntaxException(
157 message, node.getLineNumber(), node.getColumnNumber(), node.getLastLineNumber(), node.getLastColumnNumber()
158 ), source));
159 }
160
161 private List<AnnotationNode> getTargetListFromValue(AnnotationNode collector, AnnotationNode aliasAnnotationUsage, SourceUnit source) {
162 Expression memberValue = collector.getMember("value");
163 if (memberValue == null) return Collections.EMPTY_LIST;
164 if (!(memberValue instanceof ListExpression)) {
165 addError("Annotation collector expected a list of classes, but got a "+memberValue.getClass(), collector, source);
166 return Collections.EMPTY_LIST;
167 }
168 ListExpression memberListExp = (ListExpression) memberValue;
169 List<Expression> memberList = memberListExp.getExpressions();
170 if (memberList.size()==0) return Collections.EMPTY_LIST;
171 ArrayList<AnnotationNode> ret = new ArrayList<AnnotationNode>();
172 for (Expression e : memberList) {
173 AnnotationNode toAdd = new AnnotationNode(e.getType());
174 toAdd.setSourcePosition(aliasAnnotationUsage);
175 ret.add(toAdd);
176 }
177 return ret;
178 }
179
180 private List<AnnotationNode> getStoredTargetList(AnnotationNode aliasAnnotationUsage, SourceUnit source) {
181 ClassNode alias = aliasAnnotationUsage.getClassNode().redirect();
182 List<AnnotationNode> ret = getMeta(alias);
183 return copy(ret, aliasAnnotationUsage);
184 }
185
186 private List<AnnotationNode> copy(List<AnnotationNode> orig, AnnotationNode aliasAnnotationUsage) {
187 if (orig.isEmpty()) return orig;
188 List<AnnotationNode> ret = new ArrayList<AnnotationNode>(orig.size());
189 for (AnnotationNode an : orig) {
190 AnnotationNode newAn = new AnnotationNode(an.getClassNode());
191 newAn.getMembers().putAll(an.getMembers());
192 newAn.setSourcePosition(aliasAnnotationUsage);
193 ret.add(newAn);
194 }
195 return ret;
196 }
197
198 private static List<AnnotationNode> getTargetListFromAnnotations(ClassNode alias) {
199 List<AnnotationNode> annotations = alias.getAnnotations();
200 if (annotations.size() < 2) return Collections.EMPTY_LIST;
201
202 ArrayList<AnnotationNode> ret = new ArrayList<AnnotationNode>(annotations.size());
203 for (AnnotationNode an : annotations) {
204 ClassNode type = an.getClassNode();
205 if (type.getName().equals(AnnotationCollector.class.getName())) continue;
206 AnnotationNode toAdd = new AnnotationNode(type);
207 toAdd.getMembers().putAll(an.getMembers());
208 ret.add(toAdd);
209 }
210 return ret;
211 }
212
213 private static List<AnnotationNode> getTargetListFromClass(ClassNode alias) {
214 Class<?> c = alias.getTypeClass();
215 Object[][] data;
216 try {
217 Method m = c.getMethod("value");
218 data = (Object[][]) m.invoke(null);
219 } catch (Exception e) {
220 throw new GroovyBugError(e);
221 }
222 return makeListOfAnnotations(data);
223 }
224
225 private static List<AnnotationNode> makeListOfAnnotations(Object[][] data) {
226 if (data.length==0) return Collections.EMPTY_LIST;
227
228 ArrayList<AnnotationNode> ret = new ArrayList<AnnotationNode>(data.length);
229 for (Object[] inner : data) {
230 Class anno = (Class) inner[0];
231 AnnotationNode toAdd = new AnnotationNode(ClassHelper.make(anno));
232 ret.add(toAdd);
233
234 @SuppressWarnings("unchecked")
235 Map<String,Object> member = (Map<String, Object>) inner[1];
236 if (member.size()==0) continue;
237 Map<String, Expression> generated = new HashMap<String, Expression>(member.size());
238 for (String name : member.keySet()) {
239 Object val = member.get(name);
240 generated.put(name, makeExpression(val));
241 }
242 toAdd.getMembers().putAll(generated);
243 }
244 return ret;
245 }
246
247 private static Expression makeExpression(Object o) {
248 if (o instanceof Class) return new ClassExpression(ClassHelper.make((Class) o));
249
250 if (o instanceof Object[][]) {
251 List<AnnotationNode> annotations = makeListOfAnnotations((Object[][])o);
252 ListExpression le = new ListExpression();
253 for (AnnotationNode an : annotations) {
254 le.addExpression(new AnnotationConstantExpression(an));
255 }
256 return le;
257 } else if (o instanceof Object[]) {
258 ListExpression le = new ListExpression();
259 Object[] values = (Object[]) o;
260 for (Object val : values) {
261 le.addExpression(makeExpression(val));
262 }
263 return le;
264 }
265 return new ConstantExpression(o,true);
266 }
267
268
269
270
271
272
273
274
275
276 protected List<AnnotationNode> getTargetAnnotationList(AnnotationNode collector, AnnotationNode aliasAnnotationUsage, SourceUnit source) {
277 List<AnnotationNode> stored = getStoredTargetList(aliasAnnotationUsage, source);
278 List<AnnotationNode> targetList = getTargetListFromValue(collector, aliasAnnotationUsage, source);
279 int size = targetList.size()+stored.size();
280 if (size==0) return Collections.EMPTY_LIST;
281 ArrayList<AnnotationNode> ret = new ArrayList<AnnotationNode>(size);
282 ret.addAll(stored);
283 ret.addAll(targetList);
284
285 return ret;
286 }
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302 public List<AnnotationNode> visit(AnnotationNode collector, AnnotationNode aliasAnnotationUsage, AnnotatedNode aliasAnnotated, SourceUnit source) {
303 List<AnnotationNode> ret = getTargetAnnotationList(collector, aliasAnnotationUsage, source);
304 Set<String> unusedNames = new HashSet<String>(aliasAnnotationUsage.getMembers().keySet());
305
306 for (AnnotationNode an: ret) {
307 for (String name : aliasAnnotationUsage.getMembers().keySet()) {
308 if (an.getClassNode().hasMethod(name, Parameter.EMPTY_ARRAY)) {
309 unusedNames.remove(name);
310 an.setMember(name, aliasAnnotationUsage.getMember(name));
311 }
312 }
313 }
314
315 if (unusedNames.size()>0) {
316 String message = "Annotation collector got unmapped names "+unusedNames.toString()+".";
317 addError(message, aliasAnnotationUsage, source);
318 }
319
320 return ret;
321 }
322 }